/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2022-2022. All rights reserved.
 */

package com.huawei.saashousekeeper.dbpool.druid;

import lombok.Data;

import java.util.List;
import java.util.Optional;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

/**
 * Druid参数配置
 *
 * @author lWX1156935
 * @since 2022/4/22
 */
@Data
public class DruidConfig {

    private String name;

    private Integer initialSize;

    private Integer maxActive;

    private Integer minIdle;

    private Integer maxWait;

    private Long timeBetweenEvictionRunsMillis;

    private Long timeBetweenLogStatsMillis;

    private Integer statSqlMaxSize;

    private String defaultCatalog;

    private Boolean defaultAutoCommit;

    private Boolean defaultReadOnly;

    private Integer defaultTransactionIsolation;

    private Long minEvictableIdleTimeMillis;

    private Long maxEvictableIdleTimeMillis;

    private Boolean testWhileIdle;

    private Integer validationQueryTimeout;

    private Boolean useGlobalDataSourceStat;

    private Boolean asyncInit;

    private String filters;

    private Boolean clearFiltersEnable;

    private Boolean testOnBorrow;

    private Boolean testOnReturn;

    private String validationQuery;

    private Boolean resetStatEnable;

    private Integer notFullTimeoutRetryCount;

    private Integer maxWaitThreadCount;

    private Boolean failFast;

    private Long phyTimeoutMillis;

    private Boolean useUnfairLock;

    private Boolean killWhenSocketReadTimeout;

    private Boolean keepAlive;

    private Boolean poolPreparedStatements;

    private Boolean initVariants;

    private Boolean initGlobalVariants;

    private Properties connectionProperties;

    private Integer maxPoolPreparedStatementPerConnectionSize;

    private String initConnectionSqls;

    private Boolean sharePreparedStatements;

    private Boolean removeAbandoned;

    private Integer removeAbandonedTimeoutMillis;

    private Integer connectionErrorRetryAttempts;

    private Boolean breakAfterAcquireFailure;

    private Boolean logAbandoned;

    private Integer queryTimeout;

    private Integer transactionQueryTimeout;

    private String publicKey;

    private Map<String, Object> wall = new HashMap<>();

    private Map<String, Object> slf4j = new HashMap<>();

    private Map<String, Object> log4j = new HashMap<>();

    private Map<String, Object> log4j2 = new HashMap<>();

    private Map<String, Object> stat = new HashMap<>();

    private List<String> proxyFilters = new ArrayList<>();

    private Map<String, Object> commonsLog = new HashMap<>();

    /**
     * 根据全局配置和本地配置结合转换为Properties
     *
     * @return Druid配置
     */
    public Properties toProperties() {
        Properties properties = new Properties();
        Optional.ofNullable(name).ifPresent(name -> properties.setProperty(DruidConstants.FIELD_NAME, name));
        Optional.ofNullable(initialSize)
            .ifPresent(initialSize -> properties.setProperty(DruidConstants.FIELD_INITIAL_SIZE, String.valueOf(initialSize)));
        Optional.ofNullable(maxActive)
            .ifPresent(maxActive -> properties.setProperty(DruidConstants.FIELD_MAX_ACTIVE, String.valueOf(maxActive)));
        Optional.ofNullable(minIdle)
            .ifPresent(minIdle -> properties.setProperty(DruidConstants.FIELD_MIN_IDLE, String.valueOf(minIdle)));
        Optional.ofNullable(maxWait)
            .ifPresent(maxWait -> properties.setProperty(DruidConstants.FIELD_MAX_WAIT, String.valueOf(maxWait)));
        Optional.ofNullable(timeBetweenEvictionRunsMillis)
            .ifPresent(millis -> properties.setProperty(DruidConstants.FIELD_TIME_BETWEEN_EVICTION_RUNS_MILLIS,
                String.valueOf(millis)));
        Optional.ofNullable(timeBetweenLogStatsMillis)
            .ifPresent(
                millis -> properties.setProperty(DruidConstants.FIELD_TIME_BETWEEN_LOG_STATS_MILLIS, String.valueOf(millis)));
        Optional.ofNullable(minEvictableIdleTimeMillis)
            .ifPresent(
                millis -> properties.setProperty(DruidConstants.FIELD_MIN_EVICTABLE_IDLE_TIME_MILLIS, String.valueOf(millis)));
        Optional.ofNullable(maxEvictableIdleTimeMillis)
            .ifPresent(
                millis -> properties.setProperty(DruidConstants.FIELD_MAX_EVICTABLE_IDLE_TIME_MILLIS, String.valueOf(millis)));
        Optional.ofNullable(testWhileIdle)
            .ifPresent(testWhileIdle -> properties.setProperty(DruidConstants.FIELD_TEST_WHILE_IDLE, testWhileIdle.toString()));
        Optional.ofNullable(testOnBorrow)
            .ifPresent(testOnBorrow -> properties.setProperty(DruidConstants.FIELD_TEST_ON_BORROW, testOnBorrow.toString()));
        Optional.ofNullable(validationQuery)
            .ifPresent(validationQuery -> properties.setProperty(DruidConstants.FIELD_VALIDATION_QUERY, validationQuery));
        Optional.ofNullable(useGlobalDataSourceStat)
            .ifPresent(useGlobalDataSourceStat -> properties.setProperty(DruidConstants.FIELD_USE_GLOBAL_DATA_SOURCE_STAT,
                useGlobalDataSourceStat.toString()));
        Optional.ofNullable(asyncInit)
            .ifPresent(asyncInit -> properties.setProperty(DruidConstants.FIELD_ASYNC_INIT, asyncInit.toString()));
        Optional.ofNullable(clearFiltersEnable)
            .ifPresent(clearFiltersEnable -> properties.setProperty(DruidConstants.FIELD_CLEAR_FILTERS_ENABLE,
                clearFiltersEnable.toString()));
        Optional.ofNullable(resetStatEnable)
            .ifPresent(
                resetStatEnable -> properties.setProperty(DruidConstants.FIELD_RESET_STAT_ENABLE, resetStatEnable.toString()));
        Optional.ofNullable(notFullTimeoutRetryCount)
            .ifPresent(notFullTimeoutRetryCount -> properties.setProperty(DruidConstants.FIELD_NOT_FULL_TIMEOUT_RETRY_COUNT,
                String.valueOf(notFullTimeoutRetryCount)));
        Optional.ofNullable(maxWaitThreadCount)
            .ifPresent(maxWaitThreadCount -> properties.setProperty(DruidConstants.FIELD_MAX_WAIT_THREAD_COUNT,
                String.valueOf(maxWaitThreadCount)));
        Optional.ofNullable(failFast)
            .ifPresent(failFast -> properties.setProperty(DruidConstants.FIELD_FAIL_FAST, failFast.toString()));
        Optional.ofNullable(phyTimeoutMillis)
            .ifPresent(phyTimeoutMillis -> properties.setProperty(DruidConstants.FIELD_PHY_TIMEOUT_MILLIS,
                String.valueOf(phyTimeoutMillis)));
        Optional.ofNullable(keepAlive)
            .ifPresent(keepAlive -> properties.setProperty(DruidConstants.FIELD_KEEP_ALIVE, keepAlive.toString()));
        Optional.ofNullable(poolPreparedStatements)
            .ifPresent(poolPreparedStatements -> properties.setProperty(DruidConstants.FIELD_POOL_PREPARED_STATEMENTS,
                poolPreparedStatements.toString()));
        Optional.ofNullable(initVariants)
            .ifPresent(initVariants -> properties.setProperty(DruidConstants.FIELD_INIT_VARIANTS, initVariants.toString()));
        Optional.ofNullable(initGlobalVariants)
            .ifPresent(initGlobalVariants -> properties.setProperty(DruidConstants.FIELD_INIT_GLOBAL_VARIANTS,
                initGlobalVariants.toString()));
        Optional.ofNullable(useUnfairLock)
            .ifPresent(
                useUnfairLock -> properties.setProperty(DruidConstants.FIELD_USE_UNFAIR_LOCK, String.valueOf(useUnfairLock)));
        Optional.ofNullable(killWhenSocketReadTimeout)
            .ifPresent(killWhenSocketReadTimeout -> properties.setProperty(DruidConstants.FIELD_KILL_WHEN_SOCKET_READ_TIMEOUT,
                killWhenSocketReadTimeout.toString()));
        Optional.ofNullable(maxPoolPreparedStatementPerConnectionSize)
            .ifPresent(maxConnSize -> properties
                .setProperty(DruidConstants.FIELD_MAX_POOL_PREPARED_STATEMENT_PER_CONNECTION_SIZE, String.valueOf(maxConnSize)));
        Optional.ofNullable(initConnectionSqls)
            .ifPresent(
                initConnectionSqls -> properties.setProperty(DruidConstants.FIELD_INIT_CONNECTION_SQLS, initConnectionSqls));
        // filters单独处理，默认了stat
        filters = Optional.ofNullable(filters).orElse(DruidConstants.STAT_STR);
        if (publicKey != null && publicKey.length() > 0 && !filters.contains(DruidConstants.CONFIG_STR)) {
            filters += "," + DruidConstants.CONFIG_STR;
        }
        properties.setProperty(DruidConstants.FIELD_FILTERS, filters);

        if (publicKey != null && publicKey.length() > 0) {
            if (connectionProperties == null) {
                connectionProperties = new Properties();
            }
            connectionProperties.setProperty("config.decrypt", Boolean.TRUE.toString());
            connectionProperties.setProperty("config.decrypt.key", publicKey);
        }
        return properties;
    }
}